{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "stunning-reducing",
   "metadata": {},
   "source": [
    "# 面向对象"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "compliant-bankruptcy",
   "metadata": {},
   "source": [
    "* Python中 一切皆对象：\n",
    "    * class AAA: 定义的类属于 类对象(Class Object)\n",
    "    * obj1 = AAA() 属于 实例对象(Instance Object)\n",
    "* **类(Class)**: 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。  \n",
    "    * **方法**：类中定义的函数。\n",
    "        * **局部变量**：定义在方法中的变量，只作用于当前实例的类。\n",
    "    * **类变量(属性)**：类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。\n",
    "    * **继承**：即一个派生类（derived class）继承基类（base class）的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如，有这样一个设计：一个Dog类型的对象派生自Animal类，这是模拟\"是一个（is-a）\"关系（例图，Dog是一个Animal）。\n",
    "        * **方法重写**：如果从父类继承的方法不能满足子类的需求，可以对其进行改写，这个过程叫方法的覆盖（override），也称为方法的重写。\n",
    "* **实例(Instance)**：通过类定义的数据结构实例。实例包括两个数据成员（类变量和实例变量）和方法。\n",
    "    * **实例化**：创建一个类的实例，类的具体对象。\n",
    "    * **实例变量(属性)**：在类的声明中，属性是用变量来表示的，这种变量就称为实例变量，实例变量就是一个用 self 修饰的变量。\n",
    "* **数据成员**：类变量或者实例变量用于处理类及其实例对象的相关的数据。\n",
    "|    |    |\n",
    "|  ----  | ----  |\n",
    "| 类属性  | 类名.属性 |\n",
    "| 类方法  | 类名.方法(cls) |\n",
    "| 实例属性  | 属性名.属性 |\n",
    "| 实例方法  | 属性名.方法(self) |"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "arbitrary-departure",
   "metadata": {},
   "source": [
    "---\n",
    "## 类\n",
    "* 类的定义  \n",
    "可以通过类名访问其属性，类变量和方法\n",
    "```python\n",
    "class ClassName:\n",
    "    <statement-1>\n",
    "    .\n",
    "    .\n",
    "    .\n",
    "    <statement-N>\n",
    "```\n",
    "类支持两种操作：**属性引用**和**实例化** 。   \n",
    "属性引用使用和 Python 中所有的属性引用一样的标准语法：obj.name。\n",
    "\n",
    "类有一个名为 `__init__(self)` 的特殊方法（构造方法）,类的实例化操作会自动调用 `__init__(self)` 方法,可以用于实例属性的初始化以及实例个数统计。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "amino-coverage",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MyClass 类的属性 i 为： 12345\n",
      "MyClass 类的方法 f 输出为： in class\n",
      "MyClass 类的属性 i 为： 12345\n",
      "MyClass 类的方法 f 输出为： world\n",
      "MyClass 类的方法 f 输出为： world\n",
      "MyClass 类的属性 j 为： 54321 \n",
      "\n",
      "1858374734896\n",
      "j: 54321\n",
      "1858374734896\n",
      "1858374145856\n",
      "MyClass 类的方法 f 输出为： hello\n",
      "1858374734896\n"
     ]
    }
   ],
   "source": [
    "class MyClass:\n",
    "    \"\"\"一个简单的类实例\"\"\"\n",
    "    i = 12345\n",
    "    def z():\n",
    "        return 'in class'\n",
    "    def g(a):\n",
    "        print(\"j:\",a.j)\n",
    "        print(id(a))\n",
    "        a=[100,100,100]\n",
    "        print(id(a))\n",
    "        return 'hello'\n",
    "    def f(this):\n",
    "        this.j = 54321  #只有在这条语句运行之后实例才会具有j变量\n",
    "        return 'world'\n",
    "    \n",
    "# 访问实例的属性和方法\n",
    "print(\"MyClass 类的属性 i 为：\", MyClass.i)\n",
    "print(\"MyClass 类的方法 f 输出为：\", MyClass.z())\n",
    "\n",
    "# 实例化类\n",
    "x = MyClass()\n",
    "\n",
    "# 访问实例的属性和方法\n",
    "print(\"MyClass 类的属性 i 为：\", x.i)\n",
    "print(\"MyClass 类的方法 f 输出为：\", x.f())\n",
    "print(\"MyClass 类的方法 f 输出为：\", MyClass.f(x)) #这条与上一条完全等价\n",
    "print(\"MyClass 类的属性 j 为：\", x.j,'\\n')\n",
    "\n",
    "print(id(x))\n",
    "print(\"MyClass 类的方法 f 输出为：\", MyClass.g(x))\n",
    "print(id(x))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "worth-dispatch",
   "metadata": {},
   "source": [
    "---\n",
    "## 类属性和实例属性\n",
    "#### 类属性\n",
    "* 类属性 就是给 类对象 中定义的 属性\n",
    "* 通常用来记录 与这个类相关 的特征\n",
    "* 类属性 不会用于记录 具体实例的特征\n",
    "* 访问类属性有两种方式：\n",
    "    1. 类名.类属性\n",
    "    2. 对象.类属性 （不推荐）\n",
    "\n",
    "* **注意** :  \n",
    "如果使用 `实例.类属性 = 值` 赋值语句，只会 **给实例添加一个新属性**，而不会影响到 **类属性的值**\n",
    "\n",
    "#### 实例属性\n",
    "* 实例属性定义在实例方法中\n",
    "* 实例属性可以在`__init__(self)`中初始化\n",
    "* 实例属性 使用 实例方法的第一个参数(惯例上为self) 来修饰"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "continuous-wagner",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "现在创建了 3 个工具\n"
     ]
    }
   ],
   "source": [
    "class Tool(object):\n",
    "\n",
    "    # 使用赋值语句，定义类属性，记录创建工具对象的总数\n",
    "    count = 0\n",
    "\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "\n",
    "        # 针对类属性做一个计数+1\n",
    "        Tool.count += 1\n",
    "\n",
    "\n",
    "# 创建工具对象\n",
    "tool1 = Tool(\"斧头\")\n",
    "tool2 = Tool(\"榔头\")\n",
    "tool3 = Tool(\"铁锹\")\n",
    "\n",
    "# 知道使用 Tool 类到底创建了多少个对象?\n",
    "print(\"现在创建了 %d 个工具\" % Tool.count)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "stock-bracket",
   "metadata": {},
   "source": [
    "---\n",
    "## 类方法、实例方法和静态方法\n",
    "#### 类方法\n",
    "* 类方法 就是针对 类对象 定义的方法\n",
    "* 在 类方法 内部可以直接访问 类属性 或者调用其他的 类方法\n",
    "```python\n",
    "@classmethod\n",
    "def 类方法名(cls):\n",
    "    pass\n",
    "```\n",
    "* 类方法需要用 修饰器` @classmethod` 来标识，告诉解释器这是一个类方法\n",
    "    * 由 哪一个类 调用的方法，方法内的 cls 就是 哪一个类的引用\n",
    "    * 提示 使用其他名称也可以，不过惯例上使用 cls\n",
    "    * 可以通过 cls. 访问类的属性或者调用其他的类方法\n",
    "    \n",
    "#### 实例方法\n",
    "* 无任何修饰器,且具有至少一个参数的方法\n",
    "* 方法的第一参数self表示类的实例, 使用其他名称也可以，不过惯例上使用self\n",
    "* 类对象也可以访问实例方法, 但是需要带有实例参数,一般不这样做"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "wrong-thickness",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "工具对象的总数 3\n",
      "工具对象的总数 3\n",
      "['斧头', '榔头', '铁锹']\n",
      "3\n",
      "['斧头', '榔头', '铁锹']\n"
     ]
    }
   ],
   "source": [
    "class Tool(object):\n",
    "\n",
    "    # 使用赋值语句，定义类属性，记录创建工具对象的总数\n",
    "    count = 0\n",
    "    __tool_name=[]\n",
    "    \n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        Tool.__tool_name.append(name)\n",
    "        # 针对类属性做一个计数+1\n",
    "        Tool.count += 1\n",
    "        \n",
    "    @classmethod\n",
    "    def show_tool_count(cls):\n",
    "        \"\"\"显示工具对象的总数\"\"\"\n",
    "        print(\"工具对象的总数 %d\" % cls.count)\n",
    "    \n",
    "    def show_tool(cls):\n",
    "        print(cls.__tool_name)\n",
    "\n",
    "# 创建工具对象\n",
    "tool1 = Tool(\"斧头\")\n",
    "tool2 = Tool(\"榔头\")\n",
    "tool3 = Tool(\"铁锹\")\n",
    "\n",
    "# 知道使用 Tool 类到底创建了多少个对象?\n",
    "Tool.show_tool_count()\n",
    "tool1.show_tool_count()\n",
    "# Tool.show_tool() \n",
    "\"\"\" 这句运行错误,因为没有添加修饰器,\n",
    "show_tool()实际上是一个实例方法\"\"\"\n",
    "tool1.show_tool()\n",
    "\n",
    "# 类属性的访问\n",
    "print(Tool.count)\n",
    "# print(Tool.__tool_name)\n",
    "\"\"\" 这句运行错误,因为__tool_name为私有属性\n",
    "可以使用以下写法类访问,但不推荐\"\"\"\n",
    "print(Tool._Tool__tool_name)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "radio-curtis",
   "metadata": {},
   "source": [
    "#### 静态方法\n",
    "* 在开发时，如果需要在 类 中封装一个方法，这个方法：  \n",
    "既 不需要 访问 实例属性 或者调用 实例方法  \n",
    "也 不需要 访问 类属性 或者调用 类方法  \n",
    "* 这个时候，可以把这个方法封装成一个 静态方法\n",
    "```python\n",
    "@staticmethod\n",
    "def 静态方法名():\n",
    "    pass\n",
    "```\n",
    "* 静态方法 需要用 修饰器 `@staticmethod` 来标识，告诉解释器这是一个静态方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "cultural-forwarding",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "狗在跑...\n",
      "狗在跑...\n"
     ]
    }
   ],
   "source": [
    "class Dog(object):\n",
    "    \n",
    "    # 狗对象计数\n",
    "    dog_count = 0\n",
    "    \n",
    "    @staticmethod\n",
    "    def run():\n",
    "        \n",
    "        # 不需要访问实例属性也不需要访问类属性的方法\n",
    "        print(\"狗在跑...\")\n",
    "\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "dog1 = Dog(\"小白\")\n",
    "dog1.run()\n",
    "Dog.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "binary-demand",
   "metadata": {},
   "source": [
    "---\n",
    "## 继承\n",
    "* 子类（派生类 DerivedClassName）会继承父类（基类 BaseClassName）的属性和方法:\n",
    "```python\n",
    "class DerivedClassName(BaseClassName):\n",
    "    <statement-1>\n",
    "    .\n",
    "    .\n",
    "    .\n",
    "    <statement-N>\n",
    "```\n",
    "* BaseClassName（实例中的基类名）必须与派生类定义在一个作用域内。基类定义在另一个模块中时:\n",
    "```python\n",
    "class DerivedClassName(modname.BaseClassName):\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "painful-basement",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ken 说: 我 10 岁了，我在读 3 年级\n"
     ]
    }
   ],
   "source": [
    "#类定义\n",
    "class people:\n",
    "    #定义基本属性\n",
    "    name = ''\n",
    "    age = 0\n",
    "    #定义私有属性,私有属性在类外部无法直接进行访问\n",
    "    __weight = 0\n",
    "    #定义构造方法\n",
    "    def __init__(self,n,a,w):\n",
    "        self.name = n\n",
    "        self.age = a\n",
    "        self.__weight = w\n",
    "    def speak(self):\n",
    "        print(\"%s 说: 我 %d 岁。\" %(self.name,self.age))\n",
    " \n",
    "#单继承示例\n",
    "class student(people):\n",
    "    grade = ''\n",
    "    def __init__(self,n,a,w,g):\n",
    "        #调用父类的构函\n",
    "        people.__init__(self,n,a,w)\n",
    "        self.grade = g\n",
    "    #覆写父类的方法\n",
    "    def speak(self):\n",
    "        print(\"%s 说: 我 %d 岁了，我在读 %d 年级\"%(self.name,self.age,self.grade))\n",
    " \n",
    " \n",
    " \n",
    "s = student('ken',10,60,3)\n",
    "s.speak()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "emerging-horse",
   "metadata": {},
   "source": [
    "* 支持多继承形式。多继承的类定义形如下例:\n",
    "```python\n",
    "class DerivedClassName(Base1, Base2, Base3):\n",
    "    <statement-1>\n",
    "    .\n",
    "    .\n",
    "    .\n",
    "    <statement-N>\n",
    "```\n",
    "需要注意圆括号中父类的顺序，若是父类中有相同的方法名，而在子类使用时未指定，python从左至右搜索 即方法在子类中未找到时，从左到右查找父类中是否包含方法。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "driven-bouquet",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "我叫 Tim，我是一个演说家，我演讲的主题是 Python\n"
     ]
    }
   ],
   "source": [
    "#类定义\n",
    "class people:\n",
    "    #定义基本属性\n",
    "    name = ''\n",
    "    age = 0\n",
    "    #定义私有属性,私有属性在类外部无法直接进行访问\n",
    "    __weight = 0\n",
    "    #定义构造方法\n",
    "    def __init__(self,n,a,w):\n",
    "        self.name = n\n",
    "        self.age = a\n",
    "        self.__weight = w\n",
    "    def speak(self):\n",
    "        print(\"%s 说: 我 %d 岁。\" %(self.name,self.age))\n",
    " \n",
    "#单继承示例\n",
    "class student(people):\n",
    "    grade = ''\n",
    "    def __init__(self,n,a,w,g):\n",
    "        #调用父类的构函\n",
    "        people.__init__(self,n,a,w)\n",
    "        self.grade = g\n",
    "    #覆写父类的方法\n",
    "    def speak(self):\n",
    "        print(\"%s 说: 我 %d 岁了，我在读 %d 年级\"%(self.name,self.age,self.grade))\n",
    " \n",
    "#另一个类，多重继承之前的准备\n",
    "class speaker():\n",
    "    topic = ''\n",
    "    name = ''\n",
    "    def __init__(self,n,t):\n",
    "        self.name = n\n",
    "        self.topic = t\n",
    "    def speak(self):\n",
    "        print(\"我叫 %s，我是一个演说家，我演讲的主题是 %s\"%(self.name,self.topic))\n",
    " \n",
    "#多重继承\n",
    "class sample(speaker,student):\n",
    "    a =''\n",
    "    def __init__(self,n,a,w,g,t):\n",
    "        student.__init__(self,n,a,w,g)\n",
    "        speaker.__init__(self,n,t)\n",
    " \n",
    "test = sample(\"Tim\",25,80,4,\"Python\")\n",
    "test.speak()   #方法名同，默认调用的是在括号中参数位置排前父类的方法"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ahead-router",
   "metadata": {},
   "source": [
    "---\n",
    "## 方法重写\n",
    "* 如果你的父类方法的功能不能满足你的需求，你可以在子类重写你父类的方法，实例如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "labeled-stocks",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "调用子类方法\n",
      "调用父类方法\n"
     ]
    }
   ],
   "source": [
    "class Parent:        # 定义父类\n",
    "   def myMethod(self):\n",
    "      print ('调用父类方法')\n",
    "class Child(Parent): # 定义子类\n",
    "   def myMethod(self):\n",
    "      print ('调用子类方法')\n",
    "c = Child()          # 子类实例\n",
    "c.myMethod()         # 子类调用重写方法\n",
    "super(Child,c).myMethod() #用子类对象调用父类已被覆盖的方法"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "apart-stone",
   "metadata": {},
   "source": [
    "函数`super()` 是用于调用父类(超类)的一个方法"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "martial-haven",
   "metadata": {},
   "source": [
    "## 类的专有方法：\n",
    "* `__init__` : 构造函数，在生成对象时调用\n",
    "* `__del__` : 析构函数，释放对象时使用\n",
    "* `__repr__` : 打印，转换\n",
    "* `__setitem__` : 按照索引赋值\n",
    "* `__getitem__`: 按照索引获取值\n",
    "* `__len__`: 获得长度\n",
    "* `__cmp__`: 比较运算\n",
    "* `__call__`: 函数调用\n",
    "* `__add__`: 加运算\n",
    "* `__sub__`: 减运算\n",
    "* `__mul__`: 乘运算\n",
    "* `__truediv__`: 除运算\n",
    "* `__mod__`: 求余运算\n",
    "* `__pow__`: 乘方"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "timely-shoot",
   "metadata": {},
   "source": [
    "---\n",
    "## 运算符重载\n",
    "Python同样支持运算符重载，我们可以对类的专有方法进行重载，实例如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "southern-theology",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Vector (7, 8)\n"
     ]
    }
   ],
   "source": [
    "class Vector:\n",
    "   def __init__(self, a, b):\n",
    "      self.a = a\n",
    "      self.b = b\n",
    " \n",
    "   def __str__(self):\n",
    "      return 'Vector (%d, %d)' % (self.a, self.b)\n",
    "   \n",
    "   def __add__(self,other):\n",
    "      return Vector(self.a + other.a, self.b + other.b)\n",
    " \n",
    "v1 = Vector(2,10)\n",
    "v2 = Vector(5,-2)\n",
    "print (v1 + v2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "green-poultry",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python[conda env:torch38]",
   "language": "python",
   "name": "torch38"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
